package UsedCar;

import java.awt.Color;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;

import org.ahmadsoft.arrays.FasterArrayList;

import uchicago.src.sim.space.Object2DTorus;
import uchicago.src.sim.util.Random;

/**
 * @author August F.Y. Chao
 * 
 */

public class Buyer extends Agent {

	/**
	 * @param advRecived
	 *            接受到的廣告資訊
	 */
	protected Hashtable<String, List<Post>> _advRecived = new Hashtable<String, List<Post>>();
	/**
	 * @param buyerTypeString
	 *            賣家的區別字串
	 */
	protected String buyerTypeString;
	/**
	 * @param isShare
	 *            是否分享
	 */
	protected boolean isShare;
	/**
	 * 依 Zang *2 的論文所設定的參數
	 * 
	 * @param motivate_alpha
	 *            alpha 值
	 * @param motivate_beta
	 *            beta 值
	 * @param motivate_theta
	 *            theta 值
	 * @param motivate_gamma
	 *            gamma
	 */
	protected double motivate_alpha = 1.1, motivate_beta = 1.1,
			motivate_theta = 1.1, motivate_gamma = 1.1;

	/**
	 * @param need_car_price
	 *            需求車價最上值
	 */
	protected int need_car_price;

	/**
	 * @param need_car_type
	 *            需求車型
	 */
	protected int need_car_type;

	/**
	 * 下面兩個計算，會依 Buyer 的需求車型決定要評估的車子
	 * 
	 * @param pave
	 *            Pave 是以 Seller 的車子平均價格
	 * @param qave
	 *            Qave 是以 Buyer 的眼光看車子的平均價格
	 */
	protected float pave, qave;

	/**
	 * @param person_learn_max
	 *            學習知識的最大比例
	 */
	protected float person_learn_max = (float) 0.5;

	/**
	 * @param person_max_yr
	 *            最大存活年，請看 initDemand
	 */
	protected int person_max_yr = 0;
	/**
	 * @param readPostsNum
	 *            讀過的文章數
	 */
	public int readPostsNum = 0;
	/**
	 * @param 是否要重設讀取的文章
	 */
	protected boolean resetPostYr;

	/**
	 * @param sawCars
	 *            尋價過的車子
	 */
	protected List<Car> sawCars = new FasterArrayList<Car>();

	/**
	 * @param 廣告效果
	 */
	protected Impression adv = new Impression();

	/**
	 * @param space
	 *            交易平台
	 * @param knows
	 *            知識市場
	 * @param frm
	 *            知識庫存
	 * @param knowR
	 *            開始的知識比例
	 * @param eval
	 *            估價的 0 樂觀、1 悲觀、2 平平
	 * @param isShare
	 *            設定是否分享
	 * @param qual
	 *            車子的品質模式
	 * @param isInfluence
	 *            是否進行影響計算
	 * @param resetPostYr
	 *            重設文章的有效年
	 */
	public Buyer(Object2DTorus sellerS, Object2DTorus space, Forum frm,
			Knowledges knows, Quality qual, boolean isInfluence, float knowR,
			int eval, boolean isShare, boolean resetPostYr, int motiType) {

		super(sellerS, space, frm, qual, knows, isInfluence, eval);

		// 設定是否分享知識
		setShare(isShare);

		// setting knows variables
		this.setInitKnowR(knowR);
		this.initKnows();

		// reset post yr
		setResetPostYr(resetPostYr);

		this.setMyColor(Color.RED);

		// 設定角色

		this.setRole("B");
		this.initKnows();

		// 產生汽車需求
		this.initDemand();

		this.initMotivate(motiType);
	}

	/**
	 * 從學長的論文看來，好像不需要用到 alpha, beta 兩個數值
	 * 
	 * @param gamma
	 * @param value_x
	 * @return
	 */
	@SuppressWarnings("unused")
	private double _doDec(double gamma, double value_x) {
		return _doDecreasingFun(0, 1, gamma, value_x);
	}

	/**
	 * 參考學長的論文所定的效用遞減函式
	 * 
	 * @param alpha
	 *            alpha值是指最後curve趨近的高度。(低點)
	 * @param beta
	 *            beta值是指效用從最大高度降下的起始點。(高點)
	 * @param gamma
	 *            gamma值是指curve到高峰的遠近。當值很大的時候，到最高的點的x軸就會很遠，也就是需要很大量的x值才會使效用到最高點。
	 *            同理，很快到高點之後，它的差異量就不大了!
	 * @param value_x
	 *            代入的數值
	 * @return 計算的結果
	 */
	private double _doDecreasingFun(double alpha, double beta, double gamma,
			double value_x) {
		return alpha + beta * Math.exp((-1) * value_x / (gamma));
	}

	/**
	 * 從學長的論文看來，好像不需要用到 alpha, beta 兩個數值
	 * 
	 * @param gamma
	 * @param value_x
	 * @return
	 */
	@SuppressWarnings("unused")
	private double _doInc(double gamma, double value_x) {
		return _doIncreasingFun(0, 1, gamma, value_x);
	}

	/**
	 * 參考學長的論文所定的效用增加函式
	 * 
	 * @param alpha
	 *            alpha值是指與 Y 軸相交的高度，也就是當使用者原先就有的效用。(低點)
	 * @param beta
	 *            beta值是指效用增加到的最大高度。(高點)
	 * @param gamma
	 *            gamma值是指curve到高峰的遠近。當值很大的時候，到最高的點的x軸就會很遠，也就是需要很大量的x值才會使效用到最高點。
	 *            同理，很快到高點之後，它的差異量就不大了!
	 * @param value_x
	 *            代入的數值
	 * @return 計算的結果
	 */
	private double _doIncreasingFun(double alpha, double beta, double gamma,
			double value_x) {
		return alpha + beta * Math.exp((-1) * gamma / value_x);

	}

	/**
	 * 設定文章影響的賣家
	 * 
	 * @param p
	 *            影響的文章
	 */
	protected void addInflu(Post p) {
		if (resetPostYr) {
			frm.resetPost(p);
		}
		this.imp.addImp(p.influSeller, 1);
	}

	/**
	 * 存到已讀文章表內，且只存賣家的資料
	 * 
	 * @param p
	 *            文章物件
	 */
	protected void addReadPost(Post p) {

		if (p.owner.getRole().equals("S")) {
			String key = ((Seller) p.getOwner()).sellerString();

			this.adv.addImp(key, 1);
		}
	}

	/**
	 * 檢查為什麼買不到
	 */
	public void chkSawCar() {
		Iterator<Car> it = sawCars.iterator();
		while (it.hasNext()) {
			Car c = it.next();

			if (c.owner.getRole().equals("S")) {
				System.out.println("\tFor car in ("
						+ c.owner.getMyX()
						+ ", "
						+ c.owner.getMyY()
						+ ") (p,q)=("
						+ c.getEvaledPrice()
						+ ","
						+ evaluate(c)
						+ ") mv="
						+ doFormula(c.getEvaledPrice(), evaluate(c),
								c.getSellerString()));
			} else {
				System.out.println("sold");
			}
		}

	}

	/**
	 * 到市場找二手車(尋價)，後再決定跟誰買
	 */
	public void doBuyCar() {

		// for program debug
		long timeT = System.currentTimeMillis();

		// 尋價
		doSearchPrice();

		// 到要的車
		Car bCar = doFindCar();

		// 若不為空值
		if (bCar != null && bCar.owner.getRole().equals("S")) {
			String foo = "I have " + need_car_price
					+ " and am buying this Car \n";
			foo += "I think this car is: " + evaluate(bCar) + "\n";
			foo += "But selling at :" + bCar.getEvaledPrice() + "\n";
			// System.out.println(foo);
			// bCar.showActual();
			Seller man = (Seller) bCar.owner;
			man.sold(bCar, this);
			doLeave();
		}
		if (FLAG) {
			timeT = System.currentTimeMillis() - timeT;
			if (timeT > 5) {
				System.out.println("after doBuyCar() time =" + timeT);
			}
		}
	}

	/**
	 * 計算 Pave Qave 的值
	 */
	public void doComputPQ() {

		long time = System.currentTimeMillis();

		if (sawCars.size() > 0) {

			Iterator<Car> it = sawCars.iterator();

			int totalP = 0, totalQ = 0;
			while (it.hasNext()) {
				Car aCar = it.next();
				totalP += aCar.getEvaledPrice();
				totalQ += evaluate(aCar);
			}

			pave = (float) totalP / sawCars.size();
			qave = (float) totalQ / sawCars.size();

		}
		time = System.currentTimeMillis() - time;
		// if (FLAG) {
		//
		// System.out.println("(B) doComputPQ():" + time);
		// System.out.println("\tpave: " + pave + ", qave: " + qave);
		// System.out.println("\tyr=" + yr + " sawCarsNum=" + sawCars.size());
		//
		// }

	}

	/**
	 * 撿一輛可以買的車子呀!
	 * 
	 * @return 一輛車
	 */
	public Car doFindCar() {
		Car candidate = null;
		double mValue = 0;

		Iterator<Car> it = sawCars.iterator();
		String foo = "";
		while (it.hasNext()) {
			Car aCar = it.next();
			if (aCar.owner.getRole().equals("S")) {
				float carEval = evaluate(aCar);

				double val_motivate = doFormula(aCar.getEvaledPrice(), carEval,
						aCar.getSellerString());
				foo += "(saw car) I think=" + carEval + " price: "
						+ aCar.getEvaledPrice() + " motivate: " + val_motivate
						+ "\n";
				// 如果這輛車還有價的話，且我買得起的話
				// if (carEval > 0 && aCar.getEvaledPrice() <= need_car_price) {
				// 不管有沒有價，只要我買得起
				if (aCar.getEvaledPrice() <= need_car_price) {
					// 找出最大動機值的車，且是來自於賣家的
					if (val_motivate > mValue
							&& aCar.owner.getRole().equals("S")) {
						mValue = val_motivate;
						candidate = aCar;
					}
				} else {

				}
			}
		}
		// check all saw cars
		// if (candidate != null) {
		// System.out.println("I am:" + getBuyerTypeString() + " yr=" + yr
		// + " saw cars = " + sawCars.size() + " read size:"
		// + advRecived.size());
		// System.out.println(foo);
		// System.out.println("\t\t !! the mValue=" + mValue);
		// }

		// if (candidate != null) {
		// System.out.println(foo + "For car in (" + candidate.owner.getMyX()
		// + ", " + candidate.owner.getMyY() + ") (p,q)=("
		// + candidate.getEvaledPrice() + "," + evaluate(candidate)
		// + ") mv=" + mValue);
		// }
		return candidate;
	}

	/**
	 * 我覺得 theda 值(對廣告的敏感度)要改成 function
	 * 
	 * @param thisP
	 *            這車子的標價
	 * @param thisQ
	 *            我覺得這車的價
	 * @param seller_str
	 *            賣家的 tag, Car.getSellerString()
	 * @return
	 */
	public double doFormula(float thisP, float thisQ, String seller_str) {
		double diffP = Math.abs(thisP - pave), diffQ = Math.abs(thisQ - qave);

		double attP = (thisP >= pave) ? 1 : -1;
		double attQ = (thisQ >= qave) ? 1 : -1;

		double adv = 0;
		if (this.adv.isReg(seller_str)) {
			adv = (double) this.adv.getInflu4Formula(seller_str);
		}
		double influ_v = 0;
		if (this.imp.isReg(seller_str)) {
			influ_v = this.imp.getInflu4Formula(seller_str);
		}

		double p_ratio = (double) diffP / thisP * 10 + 1;
		double q_ratio = (double) diffQ / thisQ * 10 + 1;

		// if thisQ == 0 那就不要算那一項了!
		double motivate = (double) ((attP)
				* Math.pow(motivate_alpha, p_ratio)
				+ ((thisQ == 0) ? 0 : (attQ) * Math.pow(motivate_beta, q_ratio))
				+ Math.pow(motivate_theta, adv) + Math.pow(motivate_gamma,
				influ_v));
		// DecimalFormat df = new DecimalFormat("##.##");
		//
		// System.out.println("motivate = " + df.format(motivate) + " tag="
		// + getBuyerTypeString() + " " + df.format(p_ratio) + ":"
		// + df.format(q_ratio) + ":" + df.format(adv) + ":>"
		// + df.format(influ_v) + "< P1="
		// + df.format((attP) * Math.pow(motivate_alpha, p_ratio))
		// + " P2=" + df.format((attQ) * Math.pow(motivate_beta, q_ratio))
		// + " P3=" + df.format(Math.pow(motivate_theta, adv)) + " P4="
		// + df.format(Math.pow(motivate_gamma, influ_v)));

		return motivate;

		// // this is for p
		// double motive_p = attP * doDec(motivate_alpha, p_ratio);
		// // System.out.println("for P is thisP=" + thisP + " pave=" + pave
		// // + " dec=" + motive_p);
		//
		// // this is for q
		// double motive_q = attQ * doInc(motivate_beta, q_ratio);
		// // System.out.println("for Q is thisQ=" + thisQ + " qave=" + qave
		// // + " q_ratio=" + q_ratio + " motivate_beta=" + motivate_beta
		// // + " attQ=" + attQ + " inc=" + motive_q);
		//
		// // this is for adv
		// double motive_adv = doDec(motivate_theta, adv);
		//
		// // this is for influ
		// double motive_inf = doInc(motivate_gamma, influ_v);
		//
		// System.out.println(" adv=" + adv + " motive_adv=" + motive_adv
		// + " influ_v=" + influ_v + " motive_inf=" + motive_inf);
		// return (motive_p + motive_q + motive_adv + motive_inf);

		// return 1 / (double) (need_car_price - thisP);
	}

	/**
	 * 決定自己受影響的方式
	 * 
	 * @param donkowSize
	 *            不知道的知識總數
	 * @param p
	 *            文章的物件
	 */
	public void doInfluAct(int donkowSize, Post p) {
		if (p.getOwner().getRole() == "B") {
			switch (p.getInfluV()) {
			case Impression.IMP_SHARE:
				int myImp = this.imp.getVal(p.influSeller);
				if (myImp < Impression.IMP_POS) {
					addInflu(p);
				}
				break;
			case Impression.IMP_POS:
			case Impression.IMP_NEG:
				addInflu(p);
				break;
			}
		}

		if (p.getOwner().getRole() == "S" && donkowSize > 0) {
			// 加入影響的文章
			addInflu(p);
		}
	}

	@Override
	public void doKM() {
		long time;
		if (FLAG) {
			time = System.currentTimeMillis();
		}
		doReading();
		if (FLAG) {
			time = System.currentTimeMillis() - time;
			if (time > 5)
				System.out.println("(" + getRole() + ") in doReading(): "
						+ time + " ");
			time = System.currentTimeMillis();

		}
		if (isShare()) {
			doSharing();
		}
		if (FLAG) {
			time = System.currentTimeMillis() - time;
			if (time > 5)
				System.out.println("(" + getRole() + ") in doSharing(): "
						+ time + " ");
			time = System.currentTimeMillis();
		}
	}

	/**
	 * 決定是否要學習
	 * 
	 * @param know
	 *            要學的知識
	 * @return 有沒有學到
	 */
	protected boolean doLearn(String know) {
		boolean isLearn = (Random.uniform.nextIntFromTo(0, 10) < person_learn_max);
		if (isLearn) {

			String[] rKnows = new String[myKnows.length + 1];
			for (int i = 0; i < myKnows.length; i++) {
				rKnows[i] = myKnows[i];
			}
			rKnows[myKnows.length] = know;
			this.myKnows = rKnows;
			return true;
		} else
			return false;
	}

	/**
	 * 決定是否還活在市場中
	 */
	public void doLeave() {
		// 買到車了
		if (carL.size() > 0) {
			this.setMyColor(Color.BLACK);
			doLeaveAction();
		}

		// 永遠買不到
		if (yr >= person_max_yr) {
			this.setMyColor(Color.BLACK);
		}

	}

	/**
	 * 離開市場要做的事
	 */
	public void doLeaveAction() {
		Car aCar = carL.get(0);

		int influ = 1;
		if (aCar.getEvaledPrice() > aCar.ACTUAL_PRICE) {
			influ = -10;
		}

		for (int idx = 0; idx < this.maxPostNum; idx++) {
			Post art = new Post(this, (new String[0]), 1,
					Post.POST_TYPE_ARTICLE);
			art.addInflu(((Seller) aCar.buyFrom).sellerString());
			art.setInfluV(influ);
			add2Forum(art);
		}
	}

	/**
	 * 進行讀網路資料
	 */
	protected void doReading() {
		// int readNum = Random.uniform.nextIntFromTo(1, maxReadNum);
		int readNum = maxReadNum;
		this.readPostsNum += readNum;
		List<Post> posts = this.frm.getReadPosts(readNum);
		Iterator<Post> itr = posts.iterator();

		while (itr.hasNext()) {

			Post p = itr.next();

			// 取出知識並學習
			String[] postKnows = p.getPostKnows();
			String[] dontKnows = iDontKnow(postKnows);

			for (int i = 0; i < dontKnows.length; i++) {
				doLearn(dontKnows[i]);
			}

			doInfluAct(dontKnows.length, p);

			// 存到已讀文章
			addReadPost(p);
		}
	}

	/**
	 * 尋價的目的是為了要決定 Qave(猜測的平均品質) 與 Pave(店家的賣價) 尋找的店家包括網路上的店家與自己周邊的店家
	 */
	public void doSearchPrice() {
		long time = System.currentTimeMillis();

		// 清掉過去的資料
		sawCars.clear();
		// 訪問過的店家
		List<String> visited = new FasterArrayList<String>();

		// 抓出在 sX, sY 週邊的點
		// int xExt = yr / 10, yExt = yr / 10;
		// Vector<?> men = sellerS.getMooreNeighbors(getMyX(), getMyY(), xExt,
		// yExt, false);
		//
		//
		// // 跟週邊的店家尋價
		// for (int i = 0; i < men.size(); i++) {
		// Agent man = (Agent) men.get(i);
		// if (man.getRole().equals("S")) {
		// // 這個賣家
		// Seller sman = (Seller) man;
		// // 打算要賣的車
		// List<Car> cars = sman
		// .suggestCars(need_car_type, need_car_price);
		// // 加入拜訪過
		// visited.add(sman.sellerString());
		// // 加入候選車列
		// sawCars.addAll(cars);
		//
		// // 去掉重覆的車子
		// sawCars = new FasterArrayList<Car>(new HashSet<Car>(sawCars));
		// }
		// }

		// 找出過去看過的文章發文者的店家
		Enumeration<String> sellerLoc = this.adv.getAllKey();
		while (sellerLoc.hasMoreElements()) {
			String sellerStr = sellerLoc.nextElement();
			// 如果沒有拜訪過的話
			if (!visited.contains(sellerStr)) {
				String[] loc = sellerStr.split("_");
				Seller sman = (Seller) sellerS.getObjectAt(
						Integer.valueOf(loc[0]), Integer.valueOf(loc[1]));
				List<Car> cars = sman
						.suggestCars(need_car_type, need_car_price);
				sawCars.addAll(cars);
			}
		}

		// 找出所有對自己有影響的店家
		Enumeration<String> sellers = this.imp.getAllKey();
		while (sellers.hasMoreElements()) {
			String sellerStr = sellers.nextElement();
			if (sellerStr.length() > 0 && !visited.contains(sellerStr)) {
				String[] loc = sellerStr.split("_");
				Seller sman = (Seller) sellerS.getObjectAt(
						Integer.valueOf(loc[0]), Integer.valueOf(loc[1]));
				List<Car> cars = sman
						.suggestCars(need_car_type, need_car_price);
				sawCars.addAll(cars);
			}
		}

		// 去掉重覆的車子
		sawCars = new FasterArrayList<Car>(new HashSet<Car>(sawCars));

		// 計算平均賣價與平均品質
		doComputPQ();

		if (FLAG) {
			time = System.currentTimeMillis() - time;
			if (time > 5)
				System.out.println("(" + getRole() + ") doSearchPrice() :"
						+ time);
			time = System.currentTimeMillis();
		}
	}

	/**
	 * 進行知識分享
	 */
	public void doSharing() {
		int postNum = maxPostNum;
		for (int i = 0; i < postNum; i++) {
			// 文章的品質
			float quality = Random.uniform.nextIntFromTo(0, 10) / (float) 10;
			this.sharePost(quality);
		}
	}

	public String getBuyerTypeString() {
		return buyerTypeString;
	}

	/**
	 * @return sellerString = imp.getMaxKey()
	 */
	public String getMaxInflu() {
		return this.imp.getMaxKey();
	}

	// ---------------------------------------------------------------------------------------------------------------------------------------------//
	// 變數的 setter and getter
	// ---------------------------------------------------------------------------------------------------------------------------------------------//
	public int getNeed_car_price() {
		return need_car_price;
	}

	public String getString() {
		return getBuyerTypeString();
	}

	/**
	 * 建立買家需求
	 */
	public void initDemand() {

		// 需求的車型
		need_car_type = Random.uniform.nextIntFromTo(0,
				this.knowsM.getCarTypes() - 1);

		// 最長的的可存活期
		person_max_yr = 10 + Random.uniform.nextIntFromTo(-4, 4);

		// 打算花多少錢在此
		need_car_price = Random.uniform.nextIntFromTo(
				this.qual.max_quality_price / 4, this.qual.max_quality_price);

	}

	/**
	 * 買家的動機型態有三： PQ 單純以產品品質與價格為主、 AD 以廣告為主、IN 朋友推薦 為主
	 * 
	 * @param motiType
	 *            動機型態
	 */
	private void initMotivate(int motiType) {
		float increasing = 1.7f;
		switch (motiType) {
		case 1:
			setBuyerTypeString("PQ");
			motivate_alpha = increasing;
			motivate_beta = increasing;
			this.maxReadNum = 1; // 最大看文章數
			this.maxPostNum = 1; // 最看貼文數
			break;
		case 2:
			setBuyerTypeString("AD");
			this.maxReadNum = 3;
			this.maxPostNum = 1; // 最看貼文數
			motivate_theta = increasing;
			break;
		case 3:
			setBuyerTypeString("IN");
			this.maxReadNum = 3;
			this.maxPostNum = 3;
			motivate_gamma = increasing;
			break;
		}
	}

	public boolean isResetPostYr() {
		return resetPostYr;
	}

	public boolean isShare() {
		return isShare;
	}

	public void setBuyerTypeString(String buyerTypeString) {
		this.buyerTypeString = buyerTypeString;
	}

	public void setNeed_car_price(int need_car_price) {
		this.need_car_price = need_car_price;
	}

	public void setResetPostYr(boolean resetPostYr) {
		this.resetPostYr = resetPostYr;
	}

	public void setShare(boolean isShare) {
		this.isShare = isShare;
	}

	// ---------------------------------------------------------------------------------------------------------------------------------------------//
	// Over-Rides
	// ---------------------------------------------------------------------------------------------------------------------------------------------//
	@Override
	public void setVariables() {
	}

	/**
	 * 產生分享的文章
	 * 
	 * @param quality
	 *            文章品質
	 */
	public void sharePost(float quality) {
		float sharR = this.getKnowsShareR();
		String[] shareKnows = this.knowsM.getKnows(this.getMyknows(), sharR);

		Post art = new Post(this, shareKnows, quality, Post.POST_TYPE_ARTICLE);
		art.addInflu(this.getMaxInflu());
		art.setInfluV(this.imp.max_val);
		add2Forum(art);
	}
}
